package com.starview.star.code.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.starview.platform.data.type.ITypeContext;
import com.starview.platform.data.value.ResourceURI;
import com.starview.star.StarMake;
import com.starview.star.compiler.ErrorReport;
import com.starview.star.compiler.cafe.compile.Types;
import com.starview.star.compiler.sources.JavaImport;
import com.starview.star.compiler.sources.JavaInfo;

public abstract class AbstractCodeRepository implements CodeRepository
{
  private final List<RepositoryListener> listeners = new ArrayList<>();
  private final Map<String, JavaInfo> javaInfo = new HashMap<>();
  protected final ClassLoader outerLoader;
  protected final RepositoryClassLoader loader;
  protected final ErrorReport errors;

  protected AbstractCodeRepository(ClassLoader outerLoader, ErrorReport errors)
  {
    this.outerLoader = outerLoader;
    this.errors = errors;
    this.loader = new RepositoryClassLoader(this, outerLoader);
  }

  protected void setupStarLib()
  {
    StarMake.setupRepository(this, errors);
  }

  @Override
  public void addListener(RepositoryListener listener)
  {
    if (!listeners.contains(listener))
      listeners.add(listener);
  }

  @Override
  public void removeListener(RepositoryListener listener)
  {
    listeners.remove(listener);
  }

  protected void triggerUpdates(RepositoryNode node)
  {
    for (RepositoryListener listener : listeners)
      listener.nodeUpdated(node);
  }

  protected boolean triggerDelete(RepositoryNode node)
  {
    for (RepositoryListener listener : listeners)
      if (!listener.removeNode(node))
        return false;
    return true;
  }

  @Override
  public JavaInfo locateJava(String className) throws RepositoryException
  {
    JavaInfo pkg = javaInfo.get(className);

    if (pkg == null) {
      pkg = JavaImport.importJavaSchema(className, outerLoader, errors);
      if (pkg == null)
        throw new RepositoryException("cannot find java class " + className);
      javaInfo.put(className, pkg);
    }
    return pkg;
  }

  @Override
  public RepositoryClassLoader classLoader()
  {
    return loader;
  }

  @Override
  public ITypeContext loaderContext(ResourceURI pkg)
  {
    return new CodeDictionary(this, loader, pkg, errors);
  }

  @Override
  public CodeTree findCode(ResourceURI uri)
  {
    RepositoryNode node = findNode(uri);
    if (node != null)
      return node.getCode();
    return null;
  }

  @Override
  public String findHash(ResourceURI uri)
  {
    RepositoryNode node = findNode(uri);
    if (node != null)
      return node.getHash();
    else
      return null;
  }

  protected void mergeStdTypes(CodeCatalog code) throws RepositoryException
  {
    CodeCatalog genCat = genericCode().getCode();

    CodeTree compiledCode = ((CodeCatalog) code).resolve(RepositoryManager.COMPILED, null);

    if (compiledCode instanceof CodeCatalog) {
      for (Entry<String, CodeTree> entry : (CodeCatalog) compiledCode) {
        String name = entry.getKey();
        if (Types.isStdType(name) && !genCat.isPresent(name))
          genCat.addCodeEntry(name, entry.getValue());
      }
    }
  }
}
